package com.denlaku.longan.service.impl;

import com.denlaku.longan.core.Current;
import com.denlaku.longan.core.RequestContext;
import com.denlaku.longan.qo.ObjPermQuery;
import com.denlaku.longan.qo.Query;
import com.denlaku.longan.repository.TenantMemObjPermRepository;
import com.denlaku.longan.service.ObjectTagRelService;
import com.denlaku.longan.service.ObjectTagService;
import com.denlaku.longan.service.ObjectTagSetService;
import com.denlaku.longan.service.TenantMemObjPermService;
import com.denlaku.longan.util.Objects;
import com.denlaku.longan.util.SnowflakeUtils;
import com.denlaku.longan.vo.ObjectTagRel;
import com.denlaku.longan.vo.Tenant;
import com.denlaku.longan.vo.TenantMemObjPerm;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import org.apache.commons.collections4.ListUtils;
import org.jspecify.annotations.NonNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.Duration;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.function.Function;

/**
 * @author tianx
 */
@Service
public class TenantMemObjPermServiceImpl implements TenantMemObjPermService {
    private static final ConcurrentMap<Long, Cache<@NonNull Long, List<ObjectTagRel>>> CACHE = new ConcurrentHashMap<>();

    private static final Function<Long, Cache<@NonNull Long, List<ObjectTagRel>>> TENANT_CACHE_FN = tenantId -> CACHE.computeIfAbsent(
        tenantId, k -> Caffeine.newBuilder().expireAfterWrite(Duration.ofDays(7)).build());

    @Autowired
    private TenantMemObjPermRepository tenantMemObjPermRepository;

    @Autowired
    private ObjectTagSetService objectTagSetService;

    @Autowired
    private ObjectTagService objectTagService;

    @Autowired
    private ObjectTagRelService objectTagRelService;

    @Override
    public void batchAdd(List<TenantMemObjPerm> list) {
        ListUtils.emptyIfNull(list).forEach(perm -> perm.setId(SnowflakeUtils.nextId()));
        tenantMemObjPermRepository.batchAdd(list);
    }

    @Override
    public List<TenantMemObjPerm> list(Long tenantId, Long userId) {
        return tenantMemObjPermRepository.list(tenantId, userId);
    }

    @Override
    public List<ObjectTagRel> listPermTagRelList(Long tenantId, Long userId) {
        Cache<@NonNull Long, List<ObjectTagRel>> relCache = TENANT_CACHE_FN.apply(tenantId);
        return relCache.get(userId, key -> findObjectTagRelList(tenantId, userId));
    }

    private List<ObjectTagRel> findObjectTagRelList(Long tenantId, Long userId) {
        List<TenantMemObjPerm> objPerms = this.list(tenantId, userId);
        List<Long> tagSetIds = objPerms.stream().map(TenantMemObjPerm::getTagSetId).distinct().toList();

        ObjPermQuery query = new ObjPermQuery();
        query.setTenantId(tenantId);
        query.setTagSetIds(tagSetIds);
        return objectTagRelService.list(query);
    }

    public void setPermTagIds(Query query) {
        Long userId = Current.getUserId();
        RequestContext current = Current.getCurrent();
        Tenant tenant = current.getTenant();
        if (Objects.equals(tenant.getOwner(), userId)) {
            return;
        }
        Long tenantId = query.getTenantId();
        List<ObjectTagRel> tagRelList = this.listPermTagRelList(tenantId, userId);
        query.setPermTagIds(tagRelList.stream().map(ObjectTagRel::getTagId).distinct().toList());
    }

    @Override
    public void batchDelete(Long tenantId, Long userId) {
        tenantMemObjPermRepository.batchDelete(tenantId, userId);
    }

    @Override
    public void delete(Long tenantId, Long id) {
        tenantMemObjPermRepository.delete(tenantId, id);
    }
}
